﻿using Core.Network;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace Core.Res
{
    using AssetDict = Dictionary<string, WXLoader.Asset>;
    using BundleDict = Dictionary<string, WXLoader.Bundle>;

    // ReSharper disable once InconsistentNaming
    public partial class WXLoader : Component, ILoader
    {
        private AssetDict assets = new();
        private BundleDict bundles = new();
        private HashSet<string> missing = new();

        private PrefixDict mPrefixDict;
        private RelyQueue mQueue = new(10);

        public ResDict ResDict { get; } = new();

        public string RWPath;
        public string ROPath;

        public void Awake()
        {
            mPrefixDict = new PrefixDict($"Assets/Proj/Games/{G.Name}/");


            ROPath = $"{Global.OSS_DIR_PATH}/WebGL/ABS";

#if UNITY_EDITOR
            ROPath = $"{Global.CDN}/{ROPath}";
#endif

            //string fullPath = Path.Combine(ROPath, $"game.dat");

            //G.Get<NetworkSystem>().Load(fullPath, OnLoadComplete);

            DownloadVersion();
        }

        private void DownloadVersion()
        {
            string path = $"{ROPath.Replace("/ABS", "")}/VersionId.txt";

            G.Get<NetworkSystem>().Load(path, OnLoadVersionComplete);
        }

        private void OnLoadVersionComplete(IWebTask task)
        {
            if (task.Status != ETaskStatus.Done)
            {
                Debug.LogError($"Oss VersionId.txt下载失败 status={task.Status}");
                return;
            }

            string str = System.Text.Encoding.Default.GetString(task.Data);
            Debug.Log(str);
            var versions = str.Split('\n');
            var curr_version = versions[^1];
            DownloadDat(curr_version);
        }

        private void DownloadDat(string version)
        {
            string path = $"{ROPath}/game.{version}.dat";

            G.Get<NetworkSystem>().Load(path, OnLoadDatComplete);
        }

        private void OnLoadDatComplete(IWebTask task)
        {
            if (task.Status != ETaskStatus.Done)
            {
                Debug.LogError($"Oss game.dat下载失败 status={task.Status}");
                return;
            }

            string str = System.Text.Encoding.Default.GetString(task.Data);
            str = Utils.Crypto.Decrypt(str, "uertyewtruieywty");
            var resList = JsonUtility.FromJson<ResList>(str);
            ResDict.SetData(resList);
            for (int i = 0; i < resList.Bundles.Length; i++)
            {
                var vo = resList.Bundles[i];
                Debug.Log(vo.Name);
                Debug.Log(vo.MD5);
            }
        }


        //private void OnLoadComplete(IWebTask task)
        //{
        //    if (task.Status != ETaskStatus.Done)
        //    {
        //        return;
        //    }
        //    string str = System.Text.Encoding.Default.GetString(task.Data);
        //    str = Utils.Crypto.Decrypt(str, "uertyewtruieywty");
        //    var resList = JsonUtility.FromJson<ResList>(str);
        //    resDict.SetData(resList);
        //    for (int i = 0; i < resList.Bundles.Length; i++)
        //    {
        //        var vo = resList.Bundles[i];
        //        Debug.Log(vo.Name);
        //        Debug.Log(vo.MD5);
        //    }
        //}

        public bool CheckAsset(string name)
        {
            return assets.ContainsKey(name) || missing.Contains(name) || bundles.ContainsKey(name);
        }

        public IAsset GetAsset(string name)
        {
            if (string.IsNullOrWhiteSpace(name)) return null;

            name = mPrefixDict[name];

            return assets.GetValueOrDefault(name);
        }

        public IAsset LoadSync(string name)
        {
            if (string.IsNullOrWhiteSpace(name)) return null;

            name = mPrefixDict[name];

            if (assets.ContainsKey(name) && mQueue.Contains(name) == false)
            {
                return assets[name];
            }

            var assetInfo = ResDict.GetAsset(name);
            if (assetInfo == null) return null;

            var bundleInfo = ResDict.GetBundle(assetInfo.Bundle);
            if (bundleInfo == null) return null;

            var bundle = new Bundle(this);
            bundle.SetInfo(name);
            bundle.LoadSync();

            var vo = new Asset(this);
            vo.SetInfo(name);
            vo.LoadSync();

            return assets.GetValueOrDefault(name);
        }

        private List<string> mRelyAssets = new();
        private List<string> mRelyBundles = new();

        private void GatherRely(string asset_name, ref List<string> bundles, ref List<string> assets)
        {
            asset_name = mPrefixDict[asset_name];

            var assetInfo = ResDict.GetAsset(asset_name);
            if (assetInfo == null)
            {
                Debug.LogError($"Can not load asset '{asset_name}'. it's not exist");
                return;
            }

            var bundleInfo = ResDict.GetBundle(assetInfo.Bundle);
            if (bundleInfo == null)
            {
                Debug.LogError($"Can not load bundle '{assetInfo.Bundle}'. it's not exist");
                return;
            }

            if (asset_name.EndsWith(".spriteatlas") ||
                asset_name.EndsWith(@".shadervariants"))
            {
                if (assets.Contains(assetInfo.Path) == false)
                {
                    assets.Add(assetInfo.Path);
                }

                if (bundles.Contains(bundleInfo.Name) == false)
                {
                    bundles.Add(bundleInfo.Name);
                }

                return;
            }

            var relies = assetInfo.Relies;
            if (relies != null)
            {
                for (int i = 0; i < relies.Length; i++)
                {
                    GatherRely(relies[i], ref bundles, ref assets);
                }
            }

            if (assets.Contains(assetInfo.Path) == false)
            {
                assets.Add(assetInfo.Path);
            }

            if (bundles.Contains(bundleInfo.Name) == false)
            {
                bundles.Add(bundleInfo.Name);
            }
        }

        private void SetDependencyRes(string asset_name, Asset vo)
        {
            var info = ResDict.GetAsset(asset_name);
            if (mRelyBundles.Contains(info.Bundle))
            {
                vo.Rely.Add(info.Bundle);
            }

            foreach (var item in info.Relies)
            {
                if (mRelyAssets.Contains(item))
                {
                    vo.Rely.Add(item);
                }
            }
        }

        public async ETTask<IAsset> LoadAsync(string name)
        {
            if (string.IsNullOrWhiteSpace(name)) return null;

            name = mPrefixDict[name];

            if (assets.ContainsKey(name) && mQueue.Contains(name) == false)
            {
                return assets[name];
            }

            mRelyAssets.Clear();
            mRelyBundles.Clear();

            GatherRely(name, ref mRelyBundles, ref mRelyAssets);

            mRelyAssets.Remove(name);

            for (int i = 0; i < mRelyBundles.Count; i++)
            {
                var bundle_name = mRelyBundles[i];
                if (bundles.ContainsKey(bundle_name))
                {
                    continue;
                }

                Bundle vo = new Bundle(this);
                vo.SetInfo(bundle_name);
                mQueue.Enqueue(vo);
            }

            var asset = new Asset(this);
            asset.CreateAwaiter();
            asset.SetInfo(name);
            SetDependencyRes(name, asset);

            for (int i = 0; i < mRelyAssets.Count; i++)
            {
                var asset_name = mRelyAssets[i];
                if (assets.ContainsKey(asset_name)) continue;

                var vo = new Asset(this);
                vo.SetInfo(asset_name);
                mQueue.Enqueue(vo);
                SetDependencyRes(asset_name, vo);
            }

            mQueue.Enqueue(asset);

            await WaitForAsync(asset);

            if (name.EndsWith(".unity") || assets.TryGetValue(name, out var value) == false)
            {
                return null;
            }

            return value;
        }

        private ETTask WaitForAsync(LoadAwaiter vo)
        {
            return vo.Awaiter.Task;
        }

        public ETTask LoadAsync(IEnumerable<string> paths, Action<ILoadProgress> func)
        {
            mRelyAssets.Clear();
            mRelyBundles.Clear();

            foreach (var path in paths)
            {
                GatherRely(path, ref mRelyBundles, ref mRelyAssets);
            }

            for (int i = 0; i < mRelyBundles.Count; i++)
            {
                var bundle_name = mRelyBundles[i];
                if (bundles.ContainsKey(bundle_name))
                {
                    continue;
                }

                Bundle vo = new Bundle(this);
                vo.SetInfo(bundle_name);
                mQueue.Enqueue(vo);
            }

            Batch batch = new Batch(this);
            batch.CreateAwaiter();

            for (int i = 0; i < mRelyAssets.Count; i++)
            {
                var asset_name = mRelyAssets[i];
                if (assets.ContainsKey(asset_name) || asset_name.EndsWith(".unity"))
                {
                    continue;
                }

                Asset vo = new Asset(this);
                vo.SetInfo(asset_name);
                mQueue.Enqueue(vo);

                batch.Rely.Add(asset_name);
            }

            mQueue.Enqueue(batch);

            return batch.Awaiter.Task;
        }

        public void Update()
        {
            mQueue.Update();
        }
    }
}
